#/usr/bin/env python
#coding=utf-8
"""
消息轮询还需要处理
保证每个Msg都能循环发送event消息


"""
import requests
import re
import urllib
import json
import time
import random
import thread
import threading

class Log:
    def __init__(self, name, mode="wb", isNext = True):
        if not mode:mode = "wb"
        if name:
            self.f = open(name, mode)
            self.g_mutex = threading.Lock()
            if isNext:
                self.write("<====> Log Start at "+time.ctime()+" <====>")
            else:
                self.write(str(time.ctime())+"  Continue...")
        else:
            self.f = None
            self.g_mutex = None
    def write(self, line):
        if self.f:
            self.g_mutex.acquire()
            self.f.write(str(time.ctime())+"  "+line+"\r\n")
            self.f.flush()
            self.g_mutex.release()
            
    def __del__(self):
        if self.f:
            self.g_mutex.acquire()
            self.f.close()
            self.f = None
            self.g_mutex.release()
    def close(self):
        if self.f:
            self.f.close()
            self.f = None

g_log = Log("msg_log.log", "ab")
g_session_log = Log("session.log", "ab")

class Response:
    def __init__(self):
        self.status_code = -1
        self.content = ''


class Session:
    def __init__(self, timeout = 10):
        self.log = g_session_log
        self.timeout = timeout
        self.version = "1.1"
        self.s = requests.Session()
        self.s.headers = {
            "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36",
            "Accept-Language":"zh-CN,zh;q=0.8",
            "Accept":"application/json",
        }
    def post(self, url, data):
        self.s.headers.update({"Content-type":"application/x-www-form-urlencoded; charset=UTF-8"})
        tm = time.time()
        try:
            r = self.s.get(url, data=urllib.urlencode(data), timeout=self.timeout)
        except:
            self.log.write("URL:%s\r\n                          POST Failed used:%f"%(url, time.time()-tm))
            r = Response()
        else:
            self.log.write("URL:%s\r\n                          POST Success used:%f Content:"%(url, time.time()-tm)+r.content)
        return r
    def get(self, url):
        tm = time.time()
        try:
            r = self.s.get(url,timeout=self.timeout)
        except:
                                      
            self.log.write("URL:%s\r\n                          GET Failed used:%f"%(url, time.time()-tm))
            r = Response()
        else:
            self.log.write("URL:%s\r\n                          GET Success used:%f Content:"%(url, time.time()-tm)+r.content)
        return r

class Event:
    g_id = 0
    def __init__(self, handle, msg, val, tm = 0):
        Event.g_id = Event.g_id + 1
        self.id = Event.g_id
        self.time = time.time() + tm #,event的执行时间一定要大于等于这个值
        self.handle = handle
        self.msg = msg
        self.val = val

class EventHandle:
    def __init__(self, num, timer_delay = 4):#limit 限制当前消息最大条数，大于这个值，需要进行消息过滤
        self.events = []
        self.handles = []
        self.g_mutex = threading.Lock()
        self.ret = []
        self.thread_num = num
        self.thread_max = num * 10
        for i in range(num):
            thread.start_new_thread(self.proc, ())
        #启动timer
        #thread.start_new_thread(self.__timer, (timer_delay,))
    def __del__(self):
        if g_log:g_log.close()
        if g_session_log:g_session_log.close()
    def __timer(self, delay):
        while self.thread_num and self.__status:
            time.sleep(delay)
            for h in self.handles:
                if h.clientID and not h.isBusy():
                    self.__send(Event(h, 'event', ''), False)
    def add(self, handle):
        self.handles.append(handle)
    def __getHandle(self, hid):
        for h in self.handles:
            if h.msgid == hid:
                return h
        return None
    def status(self):
        online = [] 
        offline = []
        for h in self.handles:
            if h.clientID:
                online.append("%d=%s"%(h.msgid, h.msg))
            else:
                offline.append(str(h.msgid)) 

        return ( "Online:"+",".join(online), "Offline:"+",".join(offline) )
    def proc(self):
        while self.__proc():
            pass
        self.thread_num = self.thread_num - 1
    def changeServer(self, handle):
        if not handle:
            return False
        for h in self.handles:
            if h != handle and h.server(False):
                break
        return handle.server(True)
    def __proc(self):
        ret  = True
        e = self.__get()
        if not e:return ret

        if e.msg == 'event':
            status, msg = e.handle.event()
            if status == 'gotMessage':
                self.result(e, "Stranger:"+msg)
            elif status == 'disconnected':
                self.result(e, 'Disconnected')
                self.__send( Event(e.handle, 'start', '', 10), False )
                return ret
            elif status:
                self.result(e, status+":"+msg)
            if status == False:
                e.handle.times = e.handle.times + 1
            else:
                e.handle.times = 0
            self.__send( Event(e.handle, 'event', ''), False )
        elif e.msg == 'send':
            e.handle.send(e.val) 
        elif e.msg == 'start':
            val = e.handle.start()
            if val:
                val = 'Success start' #clientID='+val
                self.__send( Event(e.handle, 'event', '', 5), False )
                e.handle.times = 0
            else:
                e.handle.times = e.handle.times + 1 
                val = 'Failed  start, Try %d times'%e.handle.times
                if e.handle.times and e.handle.times%5 == 0:
                    ret = self.changeServer(e.handle)
                    self.result(e, "Change Server :"+str(ret))
                self.__send( Event(e.handle, 'start', '', 10), False )
            self.result(e, val)
        elif e.msg == 'disconnect':
            e.handle.disconnect()
        elif e.msg == 'quit':
            e.handle.disconnect()
            ret = False
        return ret
    def __get(self):
       e = None
       self.g_mutex.acquire()
       for i in range(len(self.events)):
          if self.events[i].time <= time.time():#  continue#时间未到
              e = self.events[i]
              self.events.remove(e)
              break
       self.g_mutex.release()
       return e
    def count(self):
        cnt = -1
        self.g_mutex.acquire()
        cnt = len(self.events)
        self.g_mutex.release()
        return cnt
    def __send(self, e, immediately):
        if not isinstance(e, Event):return False
        self.g_mutex.acquire()
        if immediately:
            self.events.insert(0, e)
        else:
            self.events.append(e)
        self.g_mutex.release()
        return True
    def send(self, msg, hid, val, immediately = False):#发送消息

        if isinstance(hid, tuple) or isinstance(hid, set) or isinstance(hid, list):
            for i in hid:
                h = self.__getHandle(i)
                if h:
                    self.__send(Event(h, msg, val), immediately)
        elif hid == -1:
            for h in self.handles:
                self.__send(Event(h, msg, val), immediately)
        else:
            h = self.__getHandle(hid)
            if not h:
                return False
            self.__send(Event(h, msg, val), immediately)
        return True
    def result(self, e, val):#消息处理返回值
        self.ret.append("(%03d)[%s](used:%f) "%(e.handle.msgid, e.msg, time.time()-e.time)+val)
    def show(self):
        for r in self.ret:
            print r
        self.ret = []            
    def event(self):
       msg = ""
       self.g_mutex.acquire()
       for i in range(len(self.events)):
           msg = msg + "[%d] %s,"%(self.events[i].handle.msgid, self.events[i].msg)
           if i != 0 and i % 5 == 0:
               msg = msg +"\r\n"
       self.g_mutex.release()
       return msg
class Msg:
    g_id = 0
    __servers = []
    __clients = []
    def __init__(self, host='http://front2.omegle.com'):
        Msg.g_id = Msg.g_id + 1
        self.__servers = []
        self.msgid = Msg.g_id
        self.host = host
        self.clientID = ''
        self.msg = 'waiting'#正在处理的消息
        self.session = Session()
        self.numOnline = 0
        self.m_log = Log("msg_%d.log"%self.g_id, "ab", not self.init())
        self.times = 0
    def init(self):
        try:
            f = open("msg_%d.log"%self.g_id, "rb")
        except:
            return False
        while 1:
            line = f.readline()
            if not line:break
            out = re.search("  Start ID=(?P<id>[a-z]+[a-z0-9:]+)\r\n$", line)
            if out:#考虑只获取1个小时以内的ID
                self.clientID = out.group("id")
                Msg.__clients.append(self.clientID)
        f.close()

        if self.clientID:
            return True
        else:
            return False 
    def server(self, change = False):
        ret = True
        if not Msg.__servers:
            content = self.status()
            if content:
                server = json.loads(content)
                if server:
                    server = server.get(u'servers')        
                    if server:
                        Msg.__servers = server
                    else:
                        ret = False
                else:
                    ret = False
            else:
                ret = False
        if change and Msg.__servers:
            self.host = 'http://'+Msg.__servers.pop()
            self.msgLog("Change server:"+self.host)
        return ret
    def msgLog(self, line):
        if self.m_log:
            self.m_log.write(line)
    def start(self):
        if self.clientID:return self.clientID 
        url = self.host+"/start?rcs=1&firstevents=1&spid=&randid=NEMWN4ST&lang=zh"
        self.__setMsg('start')
        r = self.session.get(url)
        if r.content:
            self.clientID = json.loads(r.content).get(u'clientID')

        if self.clientID:
            self.clientID = self.clientID.encode('utf-8')
            self.msgLog("Start ID="+self.clientID)
            if self.clientID in Msg.__clients:
                print "Have Same client(%d):"%self.msgid,self.clientID
            Msg.__clients.append(self.clientID)
        self.__setMsg()
        return self.clientID
    def disconnect(self):
        self.clientID = ""
        return ""
    def event(self):
        if not self.clientID:return ("","")
        self.__setMsg('event')
        url = self.host+"/events"
        r = self.session.post(url, {"id":self.clientID})
        self.__setMsg()
        return  self.__doEvent(r.content)
    def __setMsg(self, msg = 'waiting'):
        self.msg = msg
    def __doEvent(self, content): 
        msg = ""
        status = ""
        if not content:
            return (False, "")
        elif content == 'null':
            event = []
            status = 'disconnected'
            msg = 'null'
            Msg.__clients.remove(self.clientID)
            self.clientID = ''
            self.msgLog("Disconnected numOnline=%d"%self.numOnline)
        else:
            event = json.loads(content)
        for e in event:
            if e[0] == "waiting":
                status = 'waiting'
            elif e[0] == "connected":
                status = 'connected'
            elif e[0] == "gotMessage":
                status = 'gotMessage'
                e[1] = e[1].encode("utf-8")
                if msg:
                    msg = msg+","+e[1]
                else:
                    msg = e[1]
                self.msgLog("Stranger:"+e[1])
            elif e[0] == "strangerDisconnected":
                self.clientID = ''
                status='disconnected'
                msg=e[0]
                self.msgLog("strangerDisconnected numOnline=%d"%self.numOnline)
            elif e[0] == "count":
                self.numOnline = int(e[1])

        return (status, msg)
    def send(self, msg):
        if not self.clientID:return ""
        url = self.host+"/send"
        self.__setMsg('send')
        r = self.session.post(url, {"msg":msg, "id":self.clientID})
        if r.content != 'win':
            self.msgLog("Self(Failed):"+msg)
        else:
            self.msgLog("Self:"+msg)
        self.__setMsg()
        return r.content
    def status(self):
        url = self.host+"/status?nocache=%f&randid=NEMWN4ST"%random.random()
        r = self.session.get(url)
        return r.content
def mainMsg(num = 20):
    handle = EventHandle(num)
    for i in range(num/2+1):
        msg = Msg()
        handle.add(msg)
        handle.send('start', msg.msgid, "run begin", i*10)
    while 1:
        cmd = raw_input("cmd(event:%d)"%handle.count())
        if handle.thread_num <= 0:
            print "Quit.."
            break
        if not cmd:
            handle.show()
        elif cmd  == 'exit':#强制退出
            break
        elif cmd == 'status':
            print "Runing %d thread %d Msg ..."%( handle.thread_num, len(handle.handles) )
            print "\r\n".join(handle.status())
        elif re.match("start\d+", cmd):#不能全部重启
            hid = int(cmd[len('start'):])
            handle.send('start', hid, "cmdStart")#主动启动，需要确认没问题
        elif cmd == 'exit' or cmd == 'quit':
            handle.send("quit", -1, "", True)
        elif cmd == 'event':
            print handle.event()
        elif re.match("dis\w*\d+", cmd):
            hid = int(re.search("\d+", cmd).group())
            handle.send("disconnected", hid, "", True)
        else:
            out = re.match("(?P<id>\d+\s+|-)\s*(?P<msg>\S+.*)",cmd)
            if out:
                hid = out.group("id")
                cmd = out.group("msg")

                if hid == '-':hid = -1
                else: hid = int(hid)

                handle.send("send", hid, cmd, True)
            else:
                print "Unknow cmd:",cmd

if __name__ == "__main__":
    mainMsg()
